CPUIDLE: Support C1 FFH entry
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 30 Oct 2008 13:33:40 +0000 (13:33 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 30 Oct 2008 13:33:40 +0000 (13:33 +0000)
Add support for C1 FFH (mwait) entry. Meanwhile add timing for C1. The
timing for C1 should be accurate for FFH case, but may not for halt case.

Signed-off-by: Wei Gang <gang.wei@intel.com>
xen/arch/x86/acpi/cpu_idle.c
xen/arch/x86/acpi/cpuidle_menu.c
xen/include/xen/cpuidle.h

index eda2d9b21a2e7f108479a8782cceb3ecb0f27d5d..95f8bd4f8fd70c5d3aab11720e30d1c8cfec9402 100644 (file)
@@ -140,20 +140,26 @@ static void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)
 
 static void acpi_idle_do_entry(struct acpi_processor_cx *cx)
 {
-    if ( cx->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE )
+    int unused;
+
+    switch ( cx->entry_method )
     {
+    case ACPI_CSTATE_EM_FFH:
         /* Call into architectural FFH based C-state */
         acpi_processor_ffh_cstate_enter(cx);
-    }
-    else
-    {
-        int unused;
+        return;
+    case ACPI_CSTATE_EM_SYSIO:
         /* IO port based C-state */
         inb(cx->address);
         /* Dummy wait op - must do something useless after P_LVL2 read
            because chipsets cannot guarantee that STPCLK# signal
            gets asserted in time to freeze execution properly. */
         unused = inl(pmtmr_ioport);
+        return;
+    case ACPI_CSTATE_EM_HALT:
+        acpi_safe_halt();
+        local_irq_disable();
+        return;
     }
 }
 
@@ -253,35 +259,11 @@ static void acpi_processor_idle(void)
     switch ( cx->type )
     {
     case ACPI_STATE_C1:
-        /* Trace cpu idle entry */
-        TRACE_1D(TRC_PM_IDLE_ENTRY, 1);
-
-        /*
-         * Invoke C1.
-         * Use the appropriate idle routine, the one that would
-         * be used without acpi C-states.
-         */
-        if ( pm_idle_save )
-            pm_idle_save();
-        else 
-            acpi_safe_halt();
-
-        /* Trace cpu idle exit */
-        TRACE_1D(TRC_PM_IDLE_EXIT, 1);
-
-        /*
-         * TBD: Can't get time duration while in C1, as resumes
-         *      go to an ISR rather than here.  Need to instrument
-         *      base interrupt handler.
-         */
-        sleep_ticks = 0xFFFFFFFF;
-        break;
-
     case ACPI_STATE_C2:
-        if ( local_apic_timer_c2_ok )
+        if ( cx->type == ACPI_STATE_C1 || local_apic_timer_c2_ok )
         {
             /* Trace cpu idle entry */
-            TRACE_1D(TRC_PM_IDLE_ENTRY, 2);
+            TRACE_1D(TRC_PM_IDLE_ENTRY, cx->idx);
             /* Get start time (ticks) */
             t1 = inl(pmtmr_ioport);
             /* Invoke C2 */
@@ -289,7 +271,7 @@ static void acpi_processor_idle(void)
             /* Get end time (ticks) */
             t2 = inl(pmtmr_ioport);
             /* Trace cpu idle exit */
-            TRACE_1D(TRC_PM_IDLE_EXIT, 2);
+            TRACE_1D(TRC_PM_IDLE_EXIT, cx->idx);
 
             /* Re-enable interrupts */
             local_irq_enable();
@@ -396,6 +378,7 @@ static int init_cx_pminfo(struct acpi_processor_power *acpi_power)
         acpi_power->states[i].idx = i;
 
     acpi_power->states[ACPI_STATE_C1].type = ACPI_STATE_C1;
+    acpi_power->states[ACPI_STATE_C1].entry_method = ACPI_CSTATE_EM_HALT;
 
     acpi_power->states[ACPI_STATE_C0].valid = 1;
     acpi_power->states[ACPI_STATE_C1].valid = 1;
@@ -492,16 +475,13 @@ static int check_cx(struct acpi_processor_power *power, xen_processor_cx_t *cx)
         break;
 
     case ACPI_ADR_SPACE_FIXED_HARDWARE:
-        if ( cx->type > ACPI_STATE_C1 )
-        {
-            if ( cx->reg.bit_width != VENDOR_INTEL || 
-                 cx->reg.bit_offset != NATIVE_CSTATE_BEYOND_HALT )
-                return -EINVAL;
+        if ( cx->reg.bit_width != VENDOR_INTEL || 
+             cx->reg.bit_offset != NATIVE_CSTATE_BEYOND_HALT )
+            return -EINVAL;
 
-            /* assume all logical cpu has the same support for mwait */
-            if ( acpi_processor_ffh_cstate_probe(cx) )
-                return -EINVAL;
-        }
+        /* assume all logical cpu has the same support for mwait */
+        if ( acpi_processor_ffh_cstate_probe(cx) )
+            return -EINVAL;
         break;
 
     default:
@@ -605,7 +585,23 @@ static void set_cx(
     cx->valid    = 1;
     cx->type     = xen_cx->type;
     cx->address  = xen_cx->reg.address;
-    cx->space_id = xen_cx->reg.space_id;
+
+    switch ( xen_cx->reg.space_id )
+    {
+    case ACPI_ADR_SPACE_FIXED_HARDWARE:
+        if ( xen_cx->reg.bit_width == VENDOR_INTEL &&
+             xen_cx->reg.bit_offset == NATIVE_CSTATE_BEYOND_HALT )
+            cx->entry_method = ACPI_CSTATE_EM_FFH;
+        else
+            cx->entry_method = ACPI_CSTATE_EM_HALT;
+        break;
+    case ACPI_ADR_SPACE_SYSTEM_IO:
+        cx->entry_method = ACPI_CSTATE_EM_SYSIO;
+        break;
+    default:
+        cx->entry_method = ACPI_CSTATE_EM_NONE;
+    }
+
     cx->latency  = xen_cx->latency;
     cx->power    = xen_cx->power;
     
index 621d1bdae1584d06fa5daaa87792d8f7365fda93..683dc3bb16aa6af40357a09a85612391dccf8fa7 100644 (file)
@@ -59,7 +59,7 @@ static int menu_select(struct acpi_processor_power *power)
     data->expected_us = (u32) get_sleep_length_ns() / 1000;
 
     /* find the deepest idle state that satisfies our constraints */
-    for ( i = 1; i < power->count; i++ )
+    for ( i = 2; i < power->count; i++ )
     {
         struct acpi_processor_cx *s = &power->states[i];
 
@@ -81,17 +81,7 @@ static void menu_reflect(struct acpi_processor_power *power)
     unsigned int last_residency; 
     unsigned int measured_us;
 
-    /*
-     * Ugh, this idle state doesn't support residency measurements, so we
-     * are basically lost in the dark.  As a compromise, assume we slept
-     * for one full standard timer tick.  However, be aware that this
-     * could potentially result in a suboptimal state transition.
-     */
-    if ( target->type == ACPI_STATE_C1 )
-        last_residency = USEC_PER_SEC / HZ;
-    else
-        last_residency = power->last_residency;
-
+    last_residency = power->last_residency;
     measured_us = last_residency + data->elapsed_us;
 
     /* if wrapping, set to max uint (-1) */
index ccfae6c4d3624ae6be76b84f512330d47c57c105..559db769dcd234244a968d3fd11cbf39905330b7 100644 (file)
 #define ACPI_PROCESSOR_MAX_POWER        8
 #define CPUIDLE_NAME_LEN                16
 
+#define ACPI_CSTATE_EM_NONE     0
+#define ACPI_CSTATE_EM_SYSIO    1
+#define ACPI_CSTATE_EM_FFH      2
+#define ACPI_CSTATE_EM_HALT     3
+
 struct acpi_processor_cx
 {
     u8 idx;
     u8 valid;
     u8 type;
     u32 address;
-    u8 space_id;
+    u8 entry_method; /* ACPI_CSTATE_EM_xxx */
     u32 latency;
     u32 latency_ticks;
     u32 power;